package com.doyled.jump.user.ai;

import java.util.LinkedList;
import java.util.List;

import javax.swing.plaf.basic.BasicInternalFrameTitlePane.MoveAction;

import com.doyled.jump.object.Dice;
import com.doyled.jump.object.Horsemen;
import com.doyled.jump.object.Spot;
import com.doyled.jump.user.PlayerAI;
import com.doyled.jump.util.ChessKind;
import com.doyled.jump.util.HorseState;
import com.doyled.jump.util.SpotKind;
import com.doyled.jump.util.TeamInfo;

public class AI {

	TeamInfo teamInfo;
	int diceNumber;
	List<Spot> spots;
	List<Horsemen> horsesInDes;
	Horsemen mainHorse;
	Horsemen supporterA;
	Horsemen supporterB;
	Horsemen defender;
	PlayerAI player;

	public int getDiceNumber() {
		return diceNumber;
	}

	public void setDiceNumber(int diceNumber) {
		this.diceNumber = diceNumber;
	}

	public AI(TeamInfo teamInfo, List<Spot> spots, PlayerAI player) {
		this.teamInfo = teamInfo;
		this.spots = spots;
		this.player = player;
		mainHorse = supporterA = supporterB = defender = null;
		this.horsesInDes = new LinkedList<Horsemen>();
	}
	
	private void checkHorses() {
		if(mainHorse == null || mainHorse.getState() == HorseState.InFarm) mainHorse = supporterA;
		if(supporterA == null || supporterA.getState() == HorseState.InFarm) supporterA = defender;
	}

	public void decide() {
		checkHorses();
		int farmHorses = teamInfo.getHorsesInFarm();
		int roadHorses = teamInfo.getHorsesOnRoad();
		int desHorses = teamInfo.getHorsesInDestination();
		if (farmHorses == 4) {
			technique400();
		} else if (farmHorses == 3 && roadHorses == 1) {
			technique310();
		} else if (farmHorses == 3 && roadHorses == 0 && desHorses == 1) {
			technique301();
		} else if (farmHorses == 2 && roadHorses == 2) {
			technique220();
		} else if (farmHorses == 2 && roadHorses == 1 && desHorses == 1) { 
			technique211();
		} else if (farmHorses == 2 && roadHorses == 0 && desHorses == 2) {
			technique202();
		} else if (farmHorses == 1 && roadHorses == 3) { 
			technique130();
		} else if (farmHorses == 1 && roadHorses == 2 && desHorses == 1) {
			technique121();
		} else if (farmHorses == 1 && roadHorses == 1 && desHorses == 2) {
			technique112();
		} else if (farmHorses == 1 && roadHorses == 0 && desHorses == 3) {
			technique103();
		} else if (farmHorses == 0 && roadHorses == 3 && desHorses == 1) {
			technique031();
		} else if (farmHorses == 0 && roadHorses == 2 && desHorses == 2) {
			technique022();
		} else if (farmHorses == 0 && roadHorses == 1 && desHorses == 3) {
			technique013();
		} else if (farmHorses == 0 && roadHorses == 0 && desHorses == 4) {
			technique004();
		}
	}

	private void technique400() {
		mainHorse = player.releaseHorse();
	}

	private void technique310() {
		if (isInGate(mainHorse)) { // main horse is about to get into
									// destination
			if (!isSafe(mainHorse)) { // being in danger, main horse get into
										// destination immediately
				player.moveHorse(mainHorse);
				horsesInDes.add(mainHorse);
				mainHorse = supporterA;
			} else {
				int expectedNo = (horsesInDes.size() == 0) ? 6 : (horsesInDes.get(horsesInDes.size() - 1).getSpot().getId() - 1);
				if (diceNumber == expectedNo) { // main horse will
															// wait for expected number
					player.moveHorse(mainHorse);
					horsesInDes.add(mainHorse);
					mainHorse = supporterA;
				} else {
					if (diceNumber == 1) { // if main horse is safe, the dice
											// number is 1, release another
											// horse as defender
						defender = player.releaseHorse();
					} else player.endPhrase();
				}
			}
		} else {
			if ((diceNumber == 1 || diceNumber == 6)
					&& teamInfo.getStartSpot().getOccupiedChessKind() != teamInfo
							.getKind() && isSafe(mainHorse)) { // if main horse
																// is safe, the
																// dice number
																// is 1 or 6 and
																// main horse is
																// not in the
																// start spot,
																// release
																// another horse
																// as defender
				defender = player.releaseHorse();
			} else {
				player.moveHorse(mainHorse); // else move the main horse
			}
		}
	}

	private void technique301() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
		// all horse in des cannot move
		technique400();
	}

	private void technique220() {
		if (isInGate(mainHorse)) { // main horse is about to get into
			// destination
			if (!isSafe(mainHorse)) { // being in danger, main horse get into
				// destination immediately
				player.moveHorse(mainHorse);
				horsesInDes.add(mainHorse);
				mainHorse = supporterA;
			} else {
				if (diceNumber == 5 || diceNumber == 6) { // main horse will
					// wait for no.5 or
					// no.6 to get into
					// the destination
					player.moveHorse(mainHorse);
					horsesInDes.add(mainHorse);
					mainHorse = supporterA;
				} else {
					supporterA = defender; // change defender to support horse
					if ((diceNumber == 1)
							&& teamInfo.getStartSpot().getOccupiedChessKind() != teamInfo
									.getKind()) { // if the dice number is 1 or 6 and
													// main horse is not in the start
													// spot, release another horse as
													// defender
						defender = player.releaseHorse();
					} else if (!isOutsideOfBorder(supporterA) && canMove(supporterA)) { // supporter
																						// cannot
																						// move
																						// out
																						// of
																						// border
						player.moveHorse(supporterA);
					} else player.endPhrase();
				}
			}
		} else {
			if (isSafe(mainHorse)) { // if main horse is safe in the next turn
				supporterA = defender; // change defender to support horse
				if ((diceNumber == 1 || diceNumber == 6)
						&& teamInfo.getStartSpot().getOccupiedChessKind() != teamInfo
								.getKind()) { // if the dice number is 1 or 6 and
												// main horse is not in the start
												// spot, release another horse as
												// defender
					defender = player.releaseHorse();
				} else if (!isOutsideOfBorder(supporterA) && canMove(supporterA)) { // supporter
																					// cannot
																					// move
																					// out
																					// of
																					// border
					player.moveHorse(supporterA);
				} else {
					player.moveHorse(mainHorse);
				}
			} else { // move main horse if it is in danger
				player.moveHorse(mainHorse);
			}
		}

	}

	private void technique211() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
		// all horse in des cannot move
		technique310();
	}

	private void technique202() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
		// all horse in des cannot move
		technique400();
	}

	private void technique130() {
		if (isInGate(mainHorse)) { // main horse is about to get into
									// destination
			if (!isSafe(mainHorse)) { // being in danger, main horse get into
										// destination immediately
				player.moveHorse(mainHorse);
				horsesInDes.add(mainHorse);
				mainHorse = supporterA;
				supporterA = defender;
			} else {
				if (diceNumber == 5 || diceNumber == 6) { // main horse will
															// wait for no.5 or
															// no.6 to get into
															// the destination
					player.moveHorse(mainHorse);
					horsesInDes.add(mainHorse);
					mainHorse = supporterA;
					supporterA = defender;
				} else {
					player.moveHorse(supporterA);
				}
			}
		} else {
			if (canMove(mainHorse)) {
				player.moveHorse(mainHorse);
			} else {
				player.moveHorse(supporterA);
			}
		}
	}

	private void technique121() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
		// all horse in des cannot move
		technique220();
	}

	private void technique112() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
		// all horse in des cannot move
		technique310();
	}

	private void technique103() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
		// all horse in des cannot move
		technique400();
	}

	private void technique031() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
		// all horse in des cannot move
		if (isInGate(mainHorse)) { // main horse is about to get into
									// destination
			if (!isSafe(mainHorse)) { // being in danger, main horse get into
										// destination immediately
				if (canMove(mainHorse)) {
					player.moveHorse(mainHorse);
					horsesInDes.add(mainHorse);
					mainHorse = supporterA;
					supporterA = defender;
				} else {
					player.moveHorse(supporterA);
				}
			} else {
				int lastHorseInDesId = horsesInDes.get(horsesInDes.size() - 1)
						.getSpot().getId(); // get the last horse in destination
				if (lastHorseInDesId - 1 == diceNumber) { // main horse will
															// wait for a good
															// dice number
					player.moveHorse(mainHorse);
					horsesInDes.add(mainHorse);
					mainHorse = supporterA;
					supporterA = defender;
				} else {
					player.moveHorse(supporterA);
				}
			}
		} else {
			if (canMove(mainHorse)) {
				player.moveHorse(mainHorse);
			} else {
				player.moveHorse(supporterA);
			}
		}
	}

	private void technique022() {
		technique031();
	}

	private void technique013() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
		// all horse in des cannot move
		if (isInGate(mainHorse)) { // main horse is about to get into
									// destination
			if (canMove(mainHorse)) {
				horsesInDes.add(mainHorse);
				player.moveHorse(mainHorse);
			}
		} else {
			if (canMove(mainHorse)) {
				player.moveHorse(mainHorse);
			} else player.endPhrase();
		}
	}

	private void technique004() {
		for (Horsemen horse : horsesInDes) {
			if (canMove(horse)) {
				player.moveHorse(horse);
				return;
			}
		}
	}

	private void technique040() {
		// only use when a horse is in the gate spot

	}

	private List<Horsemen> getHorsesOnRoad() {
		LinkedList<Horsemen> horsesOnRoad = new LinkedList<Horsemen>();

		Horsemen[] horses = teamInfo.getHorses();
		for (Horsemen horse : horses) {
			if (horse.getState() == HorseState.InRoad) {
				horsesOnRoad.add(horse);
			}
		}
		return horsesOnRoad;
	}

	private List<Horsemen> getHorsesinDestination() {
		LinkedList<Horsemen> horsesInDes = new LinkedList<Horsemen>();

		Horsemen[] horses = teamInfo.getHorses();
		for (Horsemen horse : horses) {
			if (horse.getState() == HorseState.InDestination) {
				horsesInDes.add(horse);
			}
		}
		return horsesInDes;
	}

	/**
	 * check that the horse will be safe in the next turn or not
	 * 
	 * @param horsemen
	 * @return true or false
	 */
	private boolean isSafe(Horsemen horsemen) {
		ChessKind kind = teamInfo.getKind();
		int toId = horsemen.getSpot().getId();
		int fromId = toId - 6;
		fromId = (fromId < 1) ? (48 + fromId) : fromId; 
		int numOfSpot = spots.size();
		List<Spot> safeArea = new LinkedList<Spot>();
		if (fromId > toId) { // in case that horse go
														// pass the last spot of
														// board
			safeArea.addAll(spots.subList(fromId, numOfSpot));
			safeArea.addAll(spots.subList(0, toId));
		} else {
			safeArea.addAll(spots.subList(fromId, toId));
		}

		for (Spot spot : safeArea) {
			ChessKind occupiedChessKind = spot.getOccupiedChessKind();
			if (spot.isOccupied() && occupiedChessKind != ChessKind.None
					&& occupiedChessKind != teamInfo.getKind()) { // check the
																	// near
																	// spots are
																	// occupied
																	// by
																	// another
																	// team
																	// horse or
																	// not
				return false;
			}
		}
		return true;
	}

	/**
	 * check that a horse is in outside of team land or not
	 * 
	 * @return true or false
	 */
	private boolean isOutsideOfBorder(Horsemen horsemen) {
		int startSpotId = teamInfo.getStartSpot().getId(); // id of the start
															// spot
		int numberOfLandSpots = teamInfo.getTeamSpotsNumber(); // the number of
																// spots in our
																// land
		int currentSpotId = horsemen.getSpot().getId(); // id of the current
														// spot that horse is
														// occuping
		int futureSpotId = currentSpotId + diceNumber;
		return (futureSpotId >= startSpotId)
				&& (futureSpotId >= startSpotId + numberOfLandSpots);

	}

	private boolean canMove(Horsemen horsemen) {
		return !player.getMovableSpots(horsemen).isEmpty();
	}

	private boolean isInGate(Horsemen horse) {
		int gateSpotId = teamInfo.getGateSpot().getId();
		return (horse.getSpot().getId() == gateSpotId);
	}
}
